package com.settlement.system.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.settlement.system.common.annotation.PreventDuplicateSubmit;
import com.settlement.system.common.constant.SystemConstants;
import com.settlement.system.common.enums.DataGenerationEnum;
import com.settlement.system.common.exception.BusinessException;
import com.settlement.system.common.result.Result;
import com.settlement.system.common.result.ResultCode;
import com.settlement.system.common.util.StringUtils;
import com.settlement.system.listener.easyexcel.TmBillConfigConditionListener;
import com.settlement.system.listener.easyexcel.TmBillConfigListener;
import com.settlement.system.model.entity.*;
import com.settlement.system.model.form.BaseColumnForm;
import com.settlement.system.model.vo.OmsConfigImportVo;
import com.settlement.system.model.vo.TmBillConfigConditionImportVo;
import com.settlement.system.model.vo.TmBillConfigImportVo;
import com.settlement.system.service.SysBaseColumnMatchService;
import com.settlement.system.service.SysCalculateColumnService;
import com.settlement.system.service.SysColumnMatchConditionService;
import com.settlement.system.service.SysDictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>
 * 基础字段配置 前端控制器
 * </p>
 *
 * @author huangrensen
 * @since 2023-09-20
 */
@RestController
@RequestMapping("/api/v1/baseColumnMatch")
@RequiredArgsConstructor
public class SysBaseColumnMatchController {

    private final SysBaseColumnMatchService sysBaseColumnMatchService;

    private final SysCalculateColumnService calculateColumnService;

    private final SysDictTypeService dictTypeService;

    private final SysColumnMatchConditionService columnMatchConditionService;

    @Operation(summary = "保存基础字段配置", security = {@SecurityRequirement(name = "Authorization")})
    @PostMapping
    @PreAuthorize("@ss.hasPerm('sys:baseColumnMatch:save')")
    public Result<?> saveColumnMatch(@Valid @RequestBody BaseColumnForm baseColumnForm) {
        return Result.success(sysBaseColumnMatchService.saveForm(baseColumnForm));
    }

    @Operation(summary = "删除基础字段配置", security = {@SecurityRequirement(name = "Authorization")})
    @DeleteMapping("/{ids}")
    @PreAuthorize("@ss.hasPerm('sys:baseColumnMatch:delete')")
    public Result<?> deleteMatch(@Parameter(description = "基础字段配置ID，多个以英文逗号(,)分割") @PathVariable String ids) {
        boolean result = sysBaseColumnMatchService.deleteMatch(ids);
        return Result.judge(result);
    }

    @Operation(summary = "基础字段配置数据", security = {@SecurityRequirement(name = "Authorization")})
    @GetMapping("/{platformKey}/form")
    public Result<?> getColumnMatchForm(@Parameter(description = "平台KEY") @PathVariable String platformKey) {
        return Result.success(sysBaseColumnMatchService.getByPlatform(platformKey));
    }

    @Operation(summary = "检查是否配置到关联单号和业务日期", security = {@SecurityRequirement(name = "Authorization")})
    @GetMapping("/check/{platformKey}")
    public Result<?> check(@Parameter(description = "平台KEY") @PathVariable String platformKey) {
        Result<String> result = sysBaseColumnMatchService.check(platformKey);
        if (ResultCode.SUCCESS.getCode().equalsIgnoreCase(result.getCode())) {
            return result;
        }
        return Result.success(result.getMsg());
    }

    @Operation(summary = "原始账单配置导出", security = {@SecurityRequirement(name = "Authorization")})
    @GetMapping("/billConfigExport/{platformKey}")
    @PreAuthorize("@ss.hasPerm('sys:baseColumnMatch:billConfigExport')")
    @PreventDuplicateSubmit
    public void billConfigExport(HttpServletResponse response, @PathVariable String platformKey) throws Exception {
        List<TmBillConfigImportVo> importVos = new ArrayList<>();
        List<SysBaseColumnMatch> baseColumnMatches = sysBaseColumnMatchService.getByPlatform(platformKey);
        List<TmBillConfigConditionImportVo> conditionImportVoList = new ArrayList<>();
        TmBillConfigImportVo importVo;
        TmBillConfigConditionImportVo conditionImportVo;
        List<SysCalculateColumn> calculateColumnList;
        List<SysColumnMatchCondition> sysColumnMatchConditionList;
        // 配置ID
        int configId = 1;
        for (SysBaseColumnMatch baseColumnMatch : baseColumnMatches) {
            importVo = new TmBillConfigImportVo();
            // 配置ID
            importVo.setConfigId("配置%s".formatted(configId));
            // 配置ID + 1
            configId++;
            // 数据生成方式
            if (DataGenerationEnum.DIRECT.getValue().equals(baseColumnMatch.getDataGeneration())) {
                importVo.setDataGeneration(DataGenerationEnum.DIRECT.getLabel());
            } else if (DataGenerationEnum.CALCULATE.getValue().equals(baseColumnMatch.getDataGeneration())) {
                importVo.setDataGeneration(DataGenerationEnum.CALCULATE.getLabel());
            }
            // 标准账单字段
            importVo.setStandardColumn(baseColumnMatch.getStandardColumn());
            // 根据配置ID找到条件配置
            sysColumnMatchConditionList = columnMatchConditionService.getConditionList(baseColumnMatch.getId());
            if (sysColumnMatchConditionList != null && sysColumnMatchConditionList.size() > 0) {
                for (SysColumnMatchCondition condition : sysColumnMatchConditionList) {
                    conditionImportVo = new TmBillConfigConditionImportVo();
                    // 配置ID
                    conditionImportVo.setConfigId(importVo.getConfigId());
                    // 比较条件
                    conditionImportVo.setConditions(condition.getConditions());
                    // 变量
                    conditionImportVo.setParams(condition.getParams());
                    // 追加列表
                    conditionImportVoList.add(conditionImportVo);
                }
            }
            if (DataGenerationEnum.CALCULATE.getValue().equals(baseColumnMatch.getDataGeneration())) {
                // 先追加通过计算的一行配置
                importVos.add(importVo);
                // 如果数据生成方式是【通过计算】，则取出所有关联的配置
                calculateColumnList = calculateColumnService.getBaseMapper().selectList(new LambdaQueryWrapper<SysCalculateColumn>().eq(SysCalculateColumn::getLinkId, baseColumnMatch.getId()));
                if (calculateColumnList != null && calculateColumnList.size() > 0) {
                    for (SysCalculateColumn calculateColumn : calculateColumnList) {
                        importVo = new TmBillConfigImportVo();
                        // 配置ID
                        importVo.setConfigId("配置%s".formatted(configId));
                        // 配置ID + 1
                        configId++;
                        // 计算规则
                        importVo.setCalculateRule(calculateColumn.getCalculateRule());
                        // 原始账单单据类型
                        importVo.setBillType(calculateColumn.getBillType());
                        // 原始账单字段
                        importVo.setSourceColumn(calculateColumn.getSourceColumn());
                        importVos.add(importVo);
                        // 查找计算配置的条件
                        sysColumnMatchConditionList = columnMatchConditionService.getConditionList(calculateColumn.getId());
                        if (sysColumnMatchConditionList != null && sysColumnMatchConditionList.size() > 0) {
                            for (SysColumnMatchCondition condition : sysColumnMatchConditionList) {
                                conditionImportVo = new TmBillConfigConditionImportVo();
                                // 配置ID
                                conditionImportVo.setConfigId(importVo.getConfigId());
                                // 比较条件
                                conditionImportVo.setConditions(condition.getConditions());
                                // 变量
                                conditionImportVo.setParams(condition.getParams());
                                // 追加列表
                                conditionImportVoList.add(conditionImportVo);
                            }
                        }
                    }
                }
            } else {
                // 原始账单单据类型
                importVo.setBillType(baseColumnMatch.getBillType());
                // 原始账单字段
                importVo.setSourceColumn(baseColumnMatch.getSourceColumn());
                importVos.add(importVo);
            }
        }
        String fileName = "原始账单配置.xlsx";
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
        //将数据写入sheet页中
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
        if (SystemConstants.OMS_CONFIG.contains(platformKey)) {
            WriteSheet writeSheet1 = EasyExcel.writerSheet(0, "基础项目配置").head(OmsConfigImportVo.class).build();
            excelWriter.write(importVos, writeSheet1);
        } else {
            WriteSheet writeSheet1 = EasyExcel.writerSheet(0, "基础项目配置").head(TmBillConfigImportVo.class).build();
            excelWriter.write(importVos, writeSheet1);
        }
        WriteSheet writeSheet2 = EasyExcel.writerSheet(1, "符合条件").head(TmBillConfigConditionImportVo.class).build();
        excelWriter.write(conditionImportVoList, writeSheet2);
        excelWriter.finish();
        response.flushBuffer();
    }

    @Operation(summary = "原始账单配置导入", security = {@SecurityRequirement(name = "Authorization")})
    @PostMapping("/billConfigImport/{platformKey}")
    @PreAuthorize("@ss.hasPerm('sys:baseColumnMatch:billConfigImport')")
    @PreventDuplicateSubmit
    public Result<?> billConfigImport(@RequestParam("file") MultipartFile file, @Parameter(description = "平台KEY") @PathVariable String platformKey) throws Exception {
        TmBillConfigListener configListener = new TmBillConfigListener();
        TmBillConfigConditionListener configConditionListener = new TmBillConfigConditionListener();
        EasyExcelFactory.read(file.getInputStream(), TmBillConfigImportVo.class, configListener).sheet(0).doRead();
        EasyExcelFactory.read(file.getInputStream(), TmBillConfigConditionImportVo.class, configConditionListener).sheet(1).doRead();
        // 过滤出通过计算的配置
        List<TmBillConfigImportVo> calculateConfigList = new ArrayList<>();
        List<TmBillConfigImportVo> vos = configListener.getVos();
        // 过滤一些多余数据
        vos.removeIf(TmBillConfigImportVo::isEmpty);
        // 取出数据生成方式是【通过计算】的配置索引
        List<Integer> calculateIndexList = new ArrayList<>();
        int index = 0;
        for (TmBillConfigImportVo vo : vos) {
            if(checkPlatform(platformKey) && !Objects.equals(DataGenerationEnum.CALCULATE.getValue(), vo.getDataGeneration()) && !Objects.equals(DataGenerationEnum.DIRECT.getValue(), vo.getDataGeneration()) && !StringUtils.hasText(vo.getCalculateRule())){
                return Result.failed("第" + (index + 1) + "行数据生成方式有误，必须等于【直接取值】或者【通过计算】");
            }
            if (DataGenerationEnum.CALCULATE.getValue().equals(vo.getDataGeneration())) {
                calculateIndexList.add(index);
            }
            index++;
        }
        // 循环索引
        TmBillConfigImportVo current;
        TmBillConfigImportVo vo;
        List<TmBillConfigImportVo> calculateConfigIndexList;
        for (Integer calculateIndex : calculateIndexList) {
            // 初始化
            calculateConfigIndexList = new ArrayList<>();
            // 根据【通过计算】的配置往下取得所有的所有计算配置，直到数据生成方式一列为空则停止
            current = vos.get(calculateIndex);
            index = calculateIndex + 1;
            vo = vos.get(index);
            while (!StringUtils.hasText(vo.getDataGeneration())) {
                calculateConfigList.add(vo);
                calculateConfigIndexList.add(vo);
                index++;
                if (index >= vos.size()) {
                    break;
                }
                vo = vos.get(index);
            }
            // 需要计算的配置
            current.setCalculateConfigList(calculateConfigIndexList);
        }
        // 过滤【通过计算】下所有的计算配置
        vos.removeIf(item -> !StringUtils.hasText(item.getDataGeneration()) && !StringUtils.hasText(item.getStandardColumn()));
        // 所有的原始单据类型
        Set<String> billTypeSet = new HashSet<>();
        // 检查数据
        for (TmBillConfigImportVo tmBillConfigImportVo : vos) {
            Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getConfigId()), "【基础项目配置】的【配置ID】一列不能出现空值");
            if (platformKey.endsWith("order")) {
                continue;
            }
            if (checkPlatform(platformKey)) {
                Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getDataGeneration()), "【基础项目配置】的【数据生成方式】一列不能出现空值");
            }
            // 当数据生成方式为【通过计算】时，需要检查【计算规则】一列不能出现空值
            if (DataGenerationEnum.CALCULATE.getValue().equals(tmBillConfigImportVo.getDataGeneration())) {
                Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getStandardColumn()), "【基础项目配置】的【数据生成方式】为【通过计算】一列的【标准账单字段】不能出现空值");
                calculateConfigIndexList = tmBillConfigImportVo.getCalculateConfigList();
                if (calculateConfigIndexList != null && calculateConfigIndexList.size() > 0) {
                    for (TmBillConfigImportVo billConfigImportVo : calculateConfigIndexList) {
                        Assert.isTrue(StringUtils.hasText(billConfigImportVo.getCalculateRule()), "【基础项目配置】的【通过计算】下的【计算规则】一列不能为空");
                        if (checkPlatform(platformKey)) {
                            Assert.isTrue(StringUtils.hasText(billConfigImportVo.getBillType()), "【基础项目配置】的【通过计算】下的【原始账单单据类型】一列不能为空");
                        }
                        Assert.isTrue(StringUtils.hasText(billConfigImportVo.getSourceColumn()), "【基础项目配置】的【通过计算】下的【原始账单字段】一列不能为空");
                    }
                }
            } else {
                if (checkPlatform(platformKey)) {
                    Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getBillType()), "【基础项目配置】的【原始账单单据类型】一列不能为空");
                    billTypeSet.add(tmBillConfigImportVo.getBillType());
                }
                Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getSourceColumn()), "【基础项目配置】的【原始账单字段】一列不能为空");
                Assert.isTrue(StringUtils.hasText(tmBillConfigImportVo.getStandardColumn()), "【基础项目配置】的【标准账单字段】一列不能为空");
            }
        }
        // 处理符合条件的数据
        List<TmBillConfigConditionImportVo> conditionListenerVos = configConditionListener.getVos();
        // 过滤条件和变量都为空的数据
        conditionListenerVos.removeIf(item -> !StringUtils.hasText(item.getParams()) && !StringUtils.hasText(item.getConditions()));
        // 检查数据
        for (TmBillConfigConditionImportVo conditionListenerVo : conditionListenerVos) {
            Assert.isTrue(StringUtils.hasText(conditionListenerVo.getConfigId()), "【符合条件】的【基础项目配置ID】一列不能出现空值");
        }
        List<TmBillConfigConditionImportVo> voList;
        // 先处理【数据生成方式】为【直接取值】的配置条件
        for (TmBillConfigImportVo tmBillConfigImportVo : vos) {
            if (DataGenerationEnum.CALCULATE.getValue().equals(tmBillConfigImportVo.getDataGeneration())) {
                continue;
            }
            // 根据配置ID取得条件配置项
            voList = conditionListenerVos.stream().filter(item -> StringUtils.hasText(item.getConfigId()) && item.getConfigId().equals(tmBillConfigImportVo.getConfigId())).toList();
            // 检查是否存在中文符号
            if (voList.size() > 0) {
                for (TmBillConfigConditionImportVo conditionListenerVo : voList) {
                    Assert.isTrue(StringUtils.hasText(conditionListenerVo.getConditions()) && StringUtils.hasText(conditionListenerVo.getParams()), "【符合条件】的【条件】【变量】都不能为空");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("”"), "【符合条件】的【条件】一列不能出现中文【双引号】");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("“"), "【符合条件】的【条件】一列不能出现中文【双引号】");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("！"), "【符合条件】的【条件】一列不能出现中文【叹号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("，"), "【符合条件】的【变量】一列不能出现中文【逗号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("（"), "【符合条件】的【条件】一列不能出现中文【中文括号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("）"), "【符合条件】的【条件】一列不能出现中文【中文括号】");
                    for (String param : conditionListenerVo.getParams().strip().split(",")) {
                        String[] strings = param.split("的");
                        billTypeSet.add(strings[0].strip());
                    }
                }
            }
            tmBillConfigImportVo.setConfigConditionImportVoList(voList);
        }
        // 再处理【通过计算】下的字段配置的条件配置
        for (TmBillConfigImportVo tmBillConfigImportVo : calculateConfigList) {
            voList = conditionListenerVos.stream().filter(item -> StringUtils.hasText(item.getConfigId()) && item.getConfigId().equals(tmBillConfigImportVo.getConfigId())).toList();
            // 检查是否存在中文符号
            if (voList.size() > 0) {
                for (TmBillConfigConditionImportVo conditionListenerVo : voList) {
                    Assert.isTrue(StringUtils.hasText(conditionListenerVo.getConditions()) && StringUtils.hasText(conditionListenerVo.getParams()), "【符合条件】的【条件】【变量】都不能为空");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("”"), "【符合条件】的【条件】一列不能出现中文【双引号】");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("“"), "【符合条件】的【条件】一列不能出现中文【双引号】");
                    Assert.isTrue(!conditionListenerVo.getConditions().contains("！"), "【符合条件】的【条件】一列不能出现中文【叹号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("，"), "【符合条件】的【变量】一列不能出现中文【逗号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("（"), "【符合条件】的【条件】一列不能出现中文【中文括号】");
                    Assert.isTrue(!conditionListenerVo.getParams().contains("）"), "【符合条件】的【条件】一列不能出现中文【中文括号】");
                    for (String param : conditionListenerVo.getParams().strip().split(",")) {
                        String[] strings = param.split("的");
                        billTypeSet.add(strings[0].strip());
                    }
                }
            }
            tmBillConfigImportVo.setConfigConditionImportVoList(voList);
        }
        if (checkPlatform(platformKey)) {
            // 同一个平台只允许配置一个业务日期
            if (vos.stream().filter(item -> SystemConstants.BUSINESS_DATE.equals(item.getStandardColumn())).count() > 1) {
                throw new BusinessException("同一个平台只允许配置一个【业务日期】");
            }
        }
        if(billTypeSet.size() > 0){
            List<SysDictType> dictTypeList = dictTypeService.getBaseMapper().selectList(new LambdaQueryWrapper<SysDictType>().in(SysDictType::getName, new ArrayList<>(billTypeSet)));
            for (String billType : billTypeSet) {
                Assert.isTrue(dictTypeList.stream().anyMatch(item -> Objects.equals(item.getName(), billType)), "【字典管理】找不到【%s】的名称配置".formatted(billType));
            }
        }
        sysBaseColumnMatchService.saveConfig(vos, platformKey);
        return Result.success();
    }

    /**
     * 检查当前配置不是OMS相关配置
     *
     * @param platformKey 平台KEY
     * @return
     */
    private boolean checkPlatform(String platformKey) {
        return !SystemConstants.OMS_CONFIG.contains(platformKey);
    }
}
